// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package image_api_impl

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"gitcode.com/openseaotter/so_proto_gen_go.git/image_api"
	"gitcode.com/openseaotter/so_server/config"
	"gitcode.com/openseaotter/so_server/manifest_impl"
	"gitcode.com/openseaotter/so_server/storage"
)

func (apiImpl *ImageApiImpl) ListReference(ctx context.Context, req *image_api.ListReferenceRequest) (*image_api.ListReferenceResponse, error) {
	emptyList := []*image_api.ImageReference{}

	cfg, err := config.ReadServerConfigFromCache()
	if err != nil {
		return nil, err
	}
	groupInfo, err := storage.ImageGroupDao.Get(ctx, req.GroupName)
	if err != nil {
		return &image_api.ListReferenceResponse{
			Code:          image_api.ListReferenceResponse_CODE_NO_GROUP,
			ErrMsg:        "镜像分组不存在",
			ReferenceList: emptyList,
		}, nil
	}
	if cfg.SuperToken != "" && req.AccessToken == cfg.SuperToken {
		//do nothing
	} else {
		remoteCfg, ok := cfg.GetRemote(req.RemoteId)
		if !ok {
			return &image_api.ListReferenceResponse{
				Code:          image_api.ListReferenceResponse_CODE_NO_REMOTE,
				ErrMsg:        "鉴权服务器不存在",
				ReferenceList: emptyList,
			}, nil
		}
		remoteMember, _, err := storage.QueryMember(ctx, remoteCfg.RemoteAddr, req.AccessToken)
		if err != nil {
			return &image_api.ListReferenceResponse{
				Code:          image_api.ListReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg:        "没有权限",
				ReferenceList: emptyList,
			}, nil
		}
		match := false
		memberListPerm := groupInfo.PermInfo.MemberPerm[remoteMember.ProjectId]
		if memberListPerm != nil {
			for _, viewUserId := range memberListPerm.MemberUserIdForView {
				if viewUserId == remoteMember.MemberUserId {
					match = true
					break
				}
			}
		}
		if !(match || groupInfo.PermInfo.PublicGroup || remoteMember.MemberIsAdmin) {
			return &image_api.ListReferenceResponse{
				Code:          image_api.ListReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg:        "没有权限",
				ReferenceList: emptyList,
			}, nil
		}
	}

	//检查镜像是否存在
	exist, err := storage.ImageInfoDao.Exist(ctx, req.GroupName, req.ImageName)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &image_api.ListReferenceResponse{
			Code:          image_api.ListReferenceResponse_CODE_NO_IMAGE,
			ErrMsg:        "镜像信息不存在",
			ReferenceList: emptyList,
		}, nil
	}

	//列出镜像reference
	refItemList, err := storage.ImageReferenceDao.List(ctx, req.GroupName, req.ImageName, req.IncludeDigest)
	if err != nil {
		return nil, err
	}
	count := len(refItemList)
	start := int(req.Offset)
	if start >= count {
		return &image_api.ListReferenceResponse{
			Code:          image_api.ListReferenceResponse_CODE_OK,
			TotalCount:    uint32(count),
			ReferenceList: emptyList,
		}, nil
	}
	end := start + int(req.Limit)
	if end > count {
		end = count
	}
	refItemList = refItemList[start:end]

	manifestMap := map[string]*image_api.ImageManifestList{}
	if req.LoadManifest {
		for _, refItem := range refItemList {
			manifestInfo, err := apiImpl.loadManifest(refItem.ContentType, refItem.Content)
			if err != nil {
				return nil, err
			}
			manifestMap[refItem.Reference] = manifestInfo
		}
	}

	//生成结果
	retList := []*image_api.ImageReference{}
	for _, refItem := range refItemList {
		retItem := &image_api.ImageReference{
			GroupName: refItem.GroupName,
			ImageName: refItem.ImageName,
			Reference: refItem.Reference,
			TimeStamp: refItem.TimeStamp,
			Digest:    refItem.Digest,
		}
		retItem.ManifestInfo = manifestMap[refItem.Reference]
		if retItem.ManifestInfo == nil {
			retItem.ManifestInfo = &image_api.ImageManifestList{
				Manifests: []*image_api.ImageManifestList_ManifestItem{},
			}
		}

		retList = append(retList, retItem)
	}
	return &image_api.ListReferenceResponse{
		Code:          image_api.ListReferenceResponse_CODE_OK,
		TotalCount:    uint32(count),
		ReferenceList: retList,
	}, nil
}

func (apiImpl *ImageApiImpl) loadManifest(contentType, content string) (*image_api.ImageManifestList, error) {
	if contentType == manifest_impl.DOCKER_MANIFEST_TYPE {
		obj := &manifest_impl.DockerManifest{}
		err := json.Unmarshal([]byte(content), obj)
		if err != nil {
			return nil, err
		}
		manifest, err := obj.ToProto("", "")
		if err != nil {
			return nil, err
		}
		manifest.Architecture = manifest.Manifest.Config.Architecture
		manifest.Os = manifest.Manifest.Config.Os
		return &image_api.ImageManifestList{
			Manifests: []*image_api.ImageManifestList_ManifestItem{
				manifest,
			},
		}, nil
	} else if contentType == manifest_impl.OCI_MANIFEST_TYPE {
		obj := &manifest_impl.OciManifest{}
		err := json.Unmarshal([]byte(content), obj)
		if err != nil {
			return nil, err
		}
		manifest, err := obj.ToProto("", "")
		if err != nil {
			return nil, err
		}
		manifest.Architecture = manifest.Manifest.Config.Architecture
		manifest.Os = manifest.Manifest.Config.Os
		return &image_api.ImageManifestList{
			Manifests: []*image_api.ImageManifestList_ManifestItem{
				manifest,
			},
		}, nil
	} else if contentType == manifest_impl.DOCKER_MANIFEST_LSIT_TYPE {
		obj := &manifest_impl.DockerManifestList{}
		err := json.Unmarshal([]byte(content), obj)
		if err != nil {
			return nil, err
		}
		manifestList, err := obj.ToProto()
		if err != nil {
			return nil, err
		}
		return &image_api.ImageManifestList{
			Manifests: manifestList,
		}, nil
	} else if contentType == manifest_impl.OCI_MANIFEST_LIST_TYPE {
		obj := &manifest_impl.OciManifestList{}
		err := json.Unmarshal([]byte(content), obj)
		if err != nil {
			return nil, err
		}
		manifestList, err := obj.ToProto()
		if err != nil {
			return nil, err
		}
		return &image_api.ImageManifestList{
			Manifests: manifestList,
		}, nil
	}
	return nil, fmt.Errorf("unkwown contentType %s", contentType)
}

func (apiImpl *ImageApiImpl) GetReference(ctx context.Context, req *image_api.GetReferenceRequest) (*image_api.GetReferenceResponse, error) {
	emptyInfo := &image_api.ImageReference{
		ManifestInfo: &image_api.ImageManifestList{
			Manifests: []*image_api.ImageManifestList_ManifestItem{},
		},
	}

	cfg, err := config.ReadServerConfigFromCache()
	if err != nil {
		return nil, err
	}
	groupInfo, err := storage.ImageGroupDao.Get(ctx, req.GroupName)
	if err != nil {
		return &image_api.GetReferenceResponse{
			Code:      image_api.GetReferenceResponse_CODE_NO_GROUP,
			ErrMsg:    "镜像分组不存在",
			Reference: emptyInfo,
		}, nil
	}
	if cfg.SuperToken != "" && req.AccessToken == cfg.SuperToken {
		//do nothing
	} else {
		remoteCfg, ok := cfg.GetRemote(req.RemoteId)
		if !ok {
			return &image_api.GetReferenceResponse{
				Code:      image_api.GetReferenceResponse_CODE_NO_REMOTE,
				ErrMsg:    "鉴权服务器不存在",
				Reference: emptyInfo,
			}, nil
		}
		remoteMember, _, err := storage.QueryMember(ctx, remoteCfg.RemoteAddr, req.AccessToken)
		if err != nil {
			return &image_api.GetReferenceResponse{
				Code:      image_api.GetReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg:    "没有权限",
				Reference: emptyInfo,
			}, nil
		}
		match := false
		memberListPerm := groupInfo.PermInfo.MemberPerm[remoteMember.ProjectId]
		if memberListPerm != nil {
			for _, viewUserId := range memberListPerm.MemberUserIdForView {
				if viewUserId == remoteMember.MemberUserId {
					match = true
					break
				}
			}
		}
		if !(match || groupInfo.PermInfo.PublicGroup || remoteMember.MemberIsAdmin) {
			return &image_api.GetReferenceResponse{
				Code:      image_api.GetReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg:    "没有权限",
				Reference: emptyInfo,
			}, nil
		}
	}

	//检查镜像是否存在
	exist, err := storage.ImageInfoDao.Exist(ctx, req.GroupName, req.ImageName)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &image_api.GetReferenceResponse{
			Code:      image_api.GetReferenceResponse_CODE_NO_IMAGE,
			ErrMsg:    "镜像信息不存在",
			Reference: emptyInfo,
		}, nil
	}

	refItem, err := storage.ImageReferenceDao.Get(ctx, req.GroupName, req.ImageName, req.Reference)
	if err != nil {
		return &image_api.GetReferenceResponse{
			Code:      image_api.GetReferenceResponse_CODE_NO_REFERENCE,
			ErrMsg:    "镜像实例不存在",
			Reference: emptyInfo,
		}, nil
	}
	manifestInfo, err := apiImpl.loadManifest(refItem.ContentType, refItem.Content)
	if err != nil {
		return nil, err
	}

	retItem := &image_api.ImageReference{
		GroupName:    refItem.GroupName,
		ImageName:    refItem.ImageName,
		Reference:    refItem.Reference,
		ManifestInfo: manifestInfo,
		TimeStamp:    refItem.TimeStamp,
		Digest:       refItem.Digest,
	}

	return &image_api.GetReferenceResponse{
		Code:      image_api.GetReferenceResponse_CODE_OK,
		Reference: retItem,
	}, nil
}

func (apiImpl *ImageApiImpl) RemoveReference(ctx context.Context, req *image_api.RemoveReferenceRequest) (*image_api.RemoveReferenceResponse, error) {
	cfg, err := config.ReadServerConfigFromCache()
	if err != nil {
		return nil, err
	}
	groupInfo, err := storage.ImageGroupDao.Get(ctx, req.GroupName)
	if err != nil {
		return &image_api.RemoveReferenceResponse{
			Code: image_api.RemoveReferenceResponse_CODE_OK,
		}, nil
	}
	if cfg.SuperToken != "" && req.AccessToken == cfg.SuperToken {
		//do nothing
	} else {
		remoteCfg, ok := cfg.GetRemote(req.RemoteId)
		if !ok {
			return &image_api.RemoveReferenceResponse{
				Code:   image_api.RemoveReferenceResponse_CODE_NO_REMOTE,
				ErrMsg: "鉴权服务器不存在",
			}, nil
		}
		remoteMember, _, err := storage.QueryMember(ctx, remoteCfg.RemoteAddr, req.AccessToken)
		if err != nil {
			return &image_api.RemoveReferenceResponse{
				Code:   image_api.RemoveReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg: "没有权限",
			}, nil
		}
		match := false
		memberListPerm := groupInfo.PermInfo.MemberPerm[remoteMember.ProjectId]
		if memberListPerm != nil {
			for _, removeUserId := range memberListPerm.MemberUserIdForRemove {
				if removeUserId == remoteMember.MemberUserId {
					match = true
					break
				}
			}
		}
		if !match {
			return &image_api.RemoveReferenceResponse{
				Code:   image_api.RemoveReferenceResponse_CODE_NO_PERMISSION,
				ErrMsg: "没有权限",
			}, nil
		}
	}

	imageInfo, err := storage.ImageInfoDao.Get(ctx, req.GroupName, req.ImageName)
	if err != nil {
		return &image_api.RemoveReferenceResponse{
			Code: image_api.RemoveReferenceResponse_CODE_OK,
		}, nil
	}

	exist, err := storage.ImageReferenceDao.Exist(ctx, req.GroupName, req.ImageName, req.Reference)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &image_api.RemoveReferenceResponse{
			Code: image_api.RemoveReferenceResponse_CODE_OK,
		}, nil
	}
	//删除引用
	err = storage.ImageReferenceDao.Remove(ctx, req.GroupName, req.ImageName, req.Reference)
	if err != nil {
		return nil, err
	}
	//更新镜像实例计数
	count, err := storage.ImageReferenceDao.Count(ctx, req.GroupName, req.ImageName, false)
	if err != nil {
		return nil, err
	}
	imageInfo.ReferenceCount = count
	imageInfo.UpdateTime = time.Now().UnixMilli()
	err = storage.ImageInfoDao.Update(ctx, imageInfo)
	if err != nil {
		return nil, err
	}

	return &image_api.RemoveReferenceResponse{
		Code: image_api.RemoveReferenceResponse_CODE_OK,
	}, nil
}
